////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef __WN_CORE_SYSTEM_INL__
#define __WN_CORE_SYSTEM_INL__

#ifndef __WN_CORE_SYSTEM_H__
    #error "WNCore/inc/WNSystem.inl should never be included directly. Please include WNCore/inc/WNSystem.h instead"
#endif

#include "WNCore/inc/WNPlatform.h"
#include "WNCore/inc/WNConfig.h"
#include "WNCore/inc/WNSystem.h"
#include "WNCore/inc/WNTypes.h"
#include "WNCore/inc/WNMacros.h"

#ifdef _WN_WINDOWS
    #include <windows.h>

    typedef DWORD (WINAPI *__WN_GLPI_FUNCTION_T)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD);
    typedef DWORD64 (WINAPI *__WN_GEXSF_FUNCTION_T)();
#elif defined _WN_LINUX
    #include <unistd.h>
    #include <stdio.h>
#endif

#ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
    #include "WNCore/inc/WNAtomic.h"
    #include "WNCore/inc/WNAssert.h"
#endif

#define __WN_PROCESSOR_ENDIAN_BIG 1
#define __WN_PROCESSOR_ENDIAN_LITTLE (1 << 1)

#define __WN_PROCESSOR_FEATURE_FPU 1
#define __WN_PROCESSOR_FEATURE_X86_VEX_PREFIX (1 << 1)
#define __WN_PROCESSOR_FEATURE_X86_CMPXCHG8 (1 << 2)
#define __WN_PROCESSOR_FEATURE_X86_CMPXCHG16 (1 << 3)
#define __WN_PROCESSOR_FEATURE_X86_MMX (1 << 4)
#define __WN_PROCESSOR_FEATURE_X86_EMMI (1 << 5)
#define __WN_PROCESSOR_FEATURE_X86_3DNOW (1 << 6)
#define __WN_PROCESSOR_FEATURE_X86_3DNOW_PLUS (1 << 7)
#define __WN_PROCESSOR_FEATURE_X86_SSE (1 << 8)
#define __WN_PROCESSOR_FEATURE_X86_SSE2 (1 << 9)
#define __WN_PROCESSOR_FEATURE_X86_SSE3 (1 << 10)
#define __WN_PROCESSOR_FEATURE_X86_SSSE3 (1 << 11)
#define __WN_PROCESSOR_FEATURE_X86_SSE41 (1 << 12)
#define __WN_PROCESSOR_FEATURE_X86_SSE42 (1 << 13)
#define __WN_PROCESSOR_FEATURE_X86_SSE4A (1 << 14)
#define __WN_PROCESSOR_FEATURE_X86_AES (1 << 15)
#define __WN_PROCESSOR_FEATURE_X86_CLMUL (1 << 16)
#define __WN_PROCESSOR_FEATURE_X86_FMA3 (1 << 17)
#define __WN_PROCESSOR_FEATURE_X86_FMA4 (1 << 18)
#define __WN_PROCESSOR_FEATURE_X86_CVT16 (1 << 19)
#define __WN_PROCESSOR_FEATURE_X86_XOP (1 << 20)
#define __WN_PROCESSOR_FEATURE_X86_AVX (1 << 21)
#define __WN_PROCESSOR_FEATURE_X86_AVX2 (1 << 22)

struct __WNSystemData {
    WN_UINT64 mTotalPhysicalMemory;
    WN_UINT64 mTotalVirtualMemory;

    WN_UINT64 mProcessorFeatures;
    WN_UINT8 mProcessorPhysicalCoreCount;
    WN_UINT8 mProcessorLogicalThreadCount;
    WN_BYTE mProcessorEndianness;

    WN_BOOL mProcessorHyperThreaded;
};

template <typename Type>
class __WNSystemDataGlobal {
public:
    #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
        static WN_ATOM_T mOnce;
        static WN_UINT8 mThreadCount;
    #endif

    static Type mData;

    static WN_BOOL mInitialized;
};

#ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
    template <typename Type>
    WN_ATOM_T __WNSystemDataGlobal<Type>::mOnce = 0;

    template <typename Type>
    WN_UINT8 __WNSystemDataGlobal<Type>::mThreadCount = 0;
#endif

template <typename Type>
Type __WNSystemDataGlobal<Type>::mData = {0};

template <typename Type>
WN_BOOL __WNSystemDataGlobal<Type>::mInitialized = WN_FALSE;

#ifdef _WN_X86
    #ifdef _WN_MSVC
        #if _WN_MSVC < 160000000
            #pragma warning (push)
            #pragma warning (disable: 4985)
        #endif

        #include <intrin.h>

        #if _WN_MSVC < 160000000
            #pragma warning (pop)
        #endif

        #ifdef _WN_WINDOWS
            #ifndef XSTATE_MASK_GSSE
                #define __WN_XSTATE_MASK_GSSE (1i64 << 2)
            #else
                #define __WN_XSTATE_MASK_GSSE XSTATE_MASK_GSSE
            #endif
        #endif

        WN_INLINE WN_BOOL __WNHasCPUID() {
            #ifdef _WN_32_BIT
                WN_UINT32 result = 0;

                __asm {
                    pushfd
                    pop eax
                    mov ecx, eax
                    xor eax, 0x200000
                    push eax
                    popfd
                    pushfd
                    pop eax
                    xor eax, ecx
                    mov result, eax
                    push ecx
                    popfd
                }

                return(result != 0);
            #elif defined _WN_64_BIT
                return(WN_TRUE);
            #endif
        }

        WN_INLINE WN_VOID __WNCPUID(WN_INT32 _cpuInfo[4], WN_INT32 _infoType) {
            __cpuid(_cpuInfo, _infoType);
        }
    #elif defined _WN_GNUC
        WN_INLINE WN_BOOL __WNHasCPUID() {
            #ifdef _WN_32_BIT
                WN_UINT32 result = 0;

                __asm__ __volatile__ (
                    "pushfl\n"
                    "pop %%eax\n"
                    "mov %%eax, %%ecx\n"
                    "xor $0x200000, %%eax\n"
                    "push %%eax\n"
                    "popfl\n"
                    "pushfl\n"
                    "pop %%eax\n"
                    "xor %%ecx, %%eax\n"
                    "mov %%eax, %0\n"
                    "push %%ecx\n"
                    "popfl\n" :
                    "=m"(result) : :
                    "eax",
                    "ecx",
                    "memory"
                );

                return(result != 0);
            #elif defined _WN_64_BIT
                return(WN_TRUE);
            #endif
        }

        #define __WN_XCR_XFEATURE_ENABLED_MASK 0

        WN_INLINE WN_UINT64 __WNXGetBV(WN_UINT32 _x) {
            WN_UINT32 result1 = 0;
            WN_UINT32 result2 = 0;

            __asm__ __volatile__ (
                "xgetbv" :
                // Output
                "=a" (result1),
                "=d" (result2) :
                // Input
                "c" (_x)
            );

            return((static_cast<WN_UINT64>(result2) << 32) | result1);
        }

        WN_INLINE WN_VOID __WNCPUID(WN_INT32 _a[4], WN_INT32 _b) {
            __asm__ __volatile__ (
                "cpuid" :
                // Output
                "=a" (_a[0]),
                "=b" (_a[1]),
                "=c" (_a[2]),
                "=d" (_a[3]) :
                // Input
                "a" (_b)
            );
        }
    #endif
#endif

WN_INLINE WN_VOID __WNSystemDataDetermineValues() {
    __WNSystemData& systemData = __WNSystemDataGlobal<__WNSystemData>::mData;

    systemData.mProcessorFeatures = 0;
    systemData.mProcessorHyperThreaded = WN_FALSE;

    // Determine processor endianness
    #ifdef _WN_ENDIAN_LITTLE
        systemData.mProcessorEndianness = __WN_PROCESSOR_ENDIAN_LITTLE;
    #elif defined _WN_ENDIAN_BIG
        systemData.mProcessorEndianness = __WN_PROCESSOR_ENDIAN_BIG;
    #else
        const WN_UINT32 check = 1;

        if (*reinterpret_cast<const WN_BYTE*>(&check) == 1) {
            systemData.mProcessorEndianness = __WN_PROCESSOR_ENDIAN_LITTLE;
        } else {
            systemData.mProcessorEndianness = __WN_PROCESSOR_ENDIAN_BIG;
        }
    #endif

    // Determine processor physical core count and logical thread count
    #ifdef _WN_WINDOWS
        const HMODULE handle = GetModuleHandle("kernel32");
        const FARPROC address = GetProcAddress(handle, "GetLogicalProcessorInformation");
        const __WN_GLPI_FUNCTION_T GLPI = reinterpret_cast<__WN_GLPI_FUNCTION_T>(address);

        if (GLPI == WN_NULL) {
            SYSTEM_INFO sysInfo;

            GetNativeSystemInfo(&sysInfo);

            if (sysInfo.dwNumberOfProcessors > 0) {
                systemData.mProcessorLogicalThreadCount = static_cast<WN_UINT8>(sysInfo.dwNumberOfProcessors);
            }

            systemData.mProcessorPhysicalCoreCount = systemData.mProcessorLogicalThreadCount;
        } else {
            PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = WN_NULL;

            DWORD returnLength = 0;

            while (WN_TRUE) {
                const DWORD returnCode = GLPI(buffer, &returnLength);

                if (returnCode == 0) {
                    if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
                        if (buffer != WN_NULL) {
                            free(buffer);
                        }

                        buffer = static_cast<PSYSTEM_LOGICAL_PROCESSOR_INFORMATION>(malloc(returnLength));

                        if (buffer == WN_NULL) {
                            break;
                        }
                    } else {
                        break;
                    }
                } else {
                    break;
                }
            }

            if (buffer != WN_NULL) {
                systemData.mProcessorPhysicalCoreCount = 0;
                systemData.mProcessorLogicalThreadCount = 0;

                PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = buffer;

                WN_UINT32 byteOffset = 0;

                while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength) {
                    if (ptr->Relationship == RelationProcessorCore) {
                        systemData.mProcessorPhysicalCoreCount++;

                        const WN_SIZE_T shift = sizeof(WN_SIZE_T) * 8 - 1;
                        WN_SIZE_T bitTest = static_cast<WN_SIZE_T>(1) << shift;
                        WN_UINT8 bitSetCount = 0;

                        for (WN_SIZE_T i = 0; i <= shift; ++i) {
                            if ((ptr->ProcessorMask & bitTest) != 0) {
                                bitSetCount++;
                            }

                            bitTest >>= 1;
                        }

                        systemData.mProcessorLogicalThreadCount += bitSetCount;
                    }

                    byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
                    ptr++;
                }

                free(buffer);
            }
        }
    #elif defined _WN_LINUX
        // parse /proc/cpuinfo based on architecture

        systemData.mProcessorLogicalThreadCount = static_cast<WN_UINT8>(sysconf(_SC_NPROCESSORS_ONLN));
    #endif

    if (systemData.mProcessorPhysicalCoreCount < 1) {
        systemData.mProcessorPhysicalCoreCount = 1;
    }

    if (systemData.mProcessorLogicalThreadCount < 1) {
        systemData.mProcessorLogicalThreadCount = 1;
    }

    // Determine if processor is hyper-threaded
    #ifdef _WN_X86
        if (__WNHasCPUID()) {
            WN_INT32 info[4] = {0};

            __WNCPUID(info, 1);

            if (WN_BIT_TEST(info[3], 28)) {
                if (systemData.mProcessorLogicalThreadCount > systemData.mProcessorPhysicalCoreCount) {
                    systemData.mProcessorHyperThreaded = WN_TRUE;
                }
            }
        }
    #else
        if (systemData.mProcessorLogicalThreadCount > systemData.mProcessorPhysicalCoreCount) {
            systemData.mProcessorHyperThreaded = WN_TRUE;
        }
    #endif

    // Determine processor features (i.e. SSE, AVX, NEON)
    #ifdef _WN_X86
        if (__WNHasCPUID()) {
            WN_INT32 info[4] = {0};

            __WNCPUID(info, 1);

            #ifdef __WN_FPU_AVAILABLE
                systemData.mProcessorFeatures |= __WN_PROCESSOR_FEATURE_FPU;
            #else
                systemData.mProcessorFeatures |= WN_BIT_TEST(info[3], 0) ? __WN_PROCESSOR_FEATURE_FPU : 0;
            #endif

            #ifdef __WN_SSE_AVAILABLE
                systemData.mProcessorFeatures |= __WN_PROCESSOR_FEATURE_X86_SSE;
            #else
                systemData.mProcessorFeatures |= WN_BIT_TEST(info[3], 25) ? __WN_PROCESSOR_FEATURE_X86_SSE : 0;
            #endif

            #ifdef __WN_SSE2_AVAILABLE
                systemData.mProcessorFeatures |= __WN_PROCESSOR_FEATURE_X86_SSE2;
            #else
                systemData.mProcessorFeatures |= WN_BIT_TEST(info[3], 26) ? __WN_PROCESSOR_FEATURE_X86_SSE2 : 0;
            #endif

            systemData.mProcessorFeatures |= WN_BIT_TEST(info[3], 8) ? __WN_PROCESSOR_FEATURE_X86_CMPXCHG8: 0;
            systemData.mProcessorFeatures |= WN_BIT_TEST(info[2], 13) ? __WN_PROCESSOR_FEATURE_X86_CMPXCHG16: 0;
            systemData.mProcessorFeatures |= WN_BIT_TEST(info[3], 23) ? __WN_PROCESSOR_FEATURE_X86_MMX : 0;
            systemData.mProcessorFeatures |= WN_BIT_TEST(info[2], 0) ? __WN_PROCESSOR_FEATURE_X86_SSE3 : 0;
            systemData.mProcessorFeatures |= WN_BIT_TEST(info[2], 9) ? __WN_PROCESSOR_FEATURE_X86_SSSE3 : 0;
            systemData.mProcessorFeatures |= WN_BIT_TEST(info[2], 19) ? __WN_PROCESSOR_FEATURE_X86_SSE41 : 0;
            systemData.mProcessorFeatures |= WN_BIT_TEST(info[2], 20) ? __WN_PROCESSOR_FEATURE_X86_SSE42 : 0;
            systemData.mProcessorFeatures |= WN_BIT_TEST(info[2], 25) ? __WN_PROCESSOR_FEATURE_X86_AES : 0;
            systemData.mProcessorFeatures |= WN_BIT_TEST(info[2], 1) ? __WN_PROCESSOR_FEATURE_X86_CLMUL : 0;
            systemData.mProcessorFeatures |= WN_BIT_TEST(info[2], 29) ? __WN_PROCESSOR_FEATURE_X86_CVT16 : 0;

            const WN_BOOL OSXSAVESupported = WN_BIT_TEST(info[2], 27);
            WN_BOOL OSXStateEnabled = WN_FALSE;

            if (OSXSAVESupported) {
                #ifdef _WN_WINDOWS
                    const HMODULE handle = GetModuleHandle("kernel32");
                    const FARPROC address = GetProcAddress(handle, "GetEnabledXStateFeatures");
                    const __WN_GEXSF_FUNCTION_T GEXSF = reinterpret_cast<__WN_GEXSF_FUNCTION_T>(address);

                    if (GEXSF != WN_NULL) {
                        const DWORD64 featureMask = GEXSF();

                        if ((featureMask & __WN_XSTATE_MASK_GSSE) != 0) {
                            OSXStateEnabled = WN_TRUE;
                        }
                    }
                #else
                    const WN_UINT64 XCRFeatureMask = __WNXGetBV(__WN_XCR_XFEATURE_ENABLED_MASK);

                    if ((XCRFeatureMask & 0x6) == 1) {
                        OSXStateEnabled = WN_TRUE;
                    }
                #endif

                if (OSXStateEnabled) {
                    systemData.mProcessorFeatures |= __WN_PROCESSOR_FEATURE_X86_VEX_PREFIX;
                    systemData.mProcessorFeatures |= WN_BIT_TEST(info[2], 12) ? __WN_PROCESSOR_FEATURE_X86_FMA3 : 0;
                    systemData.mProcessorFeatures |= WN_BIT_TEST(info[2], 28) ? __WN_PROCESSOR_FEATURE_X86_AVX : 0;

                    __WNCPUID(info, 3);

                    systemData.mProcessorFeatures |= WN_BIT_TEST(info[1], 5) ? __WN_PROCESSOR_FEATURE_X86_AVX2 : 0;
                }
            }

            __WNCPUID(info, 0x80000000);

            if (static_cast<WN_UINT32>(info[0]) >= 0x80000001) {
                __WNCPUID(info, 0x80000001);

                systemData.mProcessorFeatures |= WN_BIT_TEST(info[3], 23) ? __WN_PROCESSOR_FEATURE_X86_EMMI : 0;
                systemData.mProcessorFeatures |= WN_BIT_TEST(info[3], 31) ? __WN_PROCESSOR_FEATURE_X86_3DNOW : 0;
                systemData.mProcessorFeatures |= WN_BIT_TEST(info[3], 30) ? __WN_PROCESSOR_FEATURE_X86_3DNOW_PLUS : 0;
                systemData.mProcessorFeatures |= WN_BIT_TEST(info[2], 6) ? __WN_PROCESSOR_FEATURE_X86_SSE4A : 0;

                if (OSXSAVESupported && OSXStateEnabled) {
                    systemData.mProcessorFeatures |= WN_BIT_TEST(info[2], 11) ? __WN_PROCESSOR_FEATURE_X86_XOP : 0;
                    systemData.mProcessorFeatures |= WN_BIT_TEST(info[2], 16) ? __WN_PROCESSOR_FEATURE_X86_FMA4 : 0;
                }
            }
        }
    #endif

    // Detemine total phyical and virtual memory
    #ifdef _WN_WINDOWS
        MEMORYSTATUSEX memoryInfo;

        memoryInfo.dwLength = sizeof(MEMORYSTATUSEX);

        GlobalMemoryStatusEx(&memoryInfo);

        systemData.mTotalPhysicalMemory = memoryInfo.ullTotalPhys;
        systemData.mTotalVirtualMemory = memoryInfo.ullTotalPageFile;
    #elif defined _WN_LINUX
        struct sysinfo memoryInfo;

        if (sysinfo(&memoryInfo) == 0) {
            systemData.mTotalPhysicalMemory = memoryInfo.totalram;
            systemData.mTotalPhysicalMemory *= memoryInfo.mem_unit;

            systemData.mTotalVirtualMemory = memoryInfo.totalram;
            systemData.mTotalVirtualMemory += memoryInfo.totalswap;
            systemData.mTotalVirtualMemory *= memoryInfo.mem_unit;
        }
    #endif
}

#ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
    WN_INLINE WN_VOID __WNSystemDataInitialize() {
        WN_ATOM_T* once = &__WNSystemDataGlobal<__WNSystemData>::mOnce;

        while (WN_TRUE) {
            const WN_ATOM_T previous = WNAtomicCompareSwap(once, 1, 0);

            if (previous == 2) {
                return;
            } else if (previous == 0) {
                break;
            } else {
                WN_RELEASE_ASSERT(previous == 1);

                WN_UINT8& threadCount = __WNSystemDataGlobal<__WNSystemData>::mThreadCount;

                if (threadCount == 0) {
                    #ifdef _WN_WINDOWS
                        SYSTEM_INFO sysInfo;

                        GetNativeSystemInfo(&sysInfo);

                        threadCount = static_cast<WN_UINT8>(sysInfo.dwNumberOfProcessors);
                    #elif defined _WN_LINUX
                        threadCount = static_cast<WN_UINT8>(sysconf(_SC_NPROCESSORS_ONLN));
                    #endif
                }

                if (threadCount > 1) {
                    WN_YIELD_PROCESSOR();
                } else{
                    #ifdef _WN_WINDOWS
                        SwitchToThread();
                    #elif defined _WN_LINUX
                        pthread_yield();
                    #endif
                }
            }
        }

        __WNSystemDataDetermineValues();

        WNAtomicSwap(once, 2);
    }
#endif

WN_INLINE WN_VOID WNSystem::Initialize() {
    if (!__WNSystemDataGlobal<__WNSystemData>::mInitialized) {
        #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
            __WNSystemDataInitialize();
        #else
            __WNSystemDataDetermineValues();
        #endif

        __WNSystemDataGlobal<__WNSystemData>::mInitialized = WN_TRUE;
    }
}

WN_INLINE WN_UINT8 WNSystem::PhysicalCoreCount() {
    #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
        __WNSystemDataInitialize();
    #endif

    return(__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorPhysicalCoreCount);
}

WN_INLINE WN_UINT8 WNSystem::LogicalThreadCount() {
    #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
        __WNSystemDataInitialize();
    #endif

    return(__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorLogicalThreadCount);
}

WN_INLINE WN_UINT64 WNSystem::TotalPhysicalMemory() {
    #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
        __WNSystemDataInitialize();
    #endif

    return(__WNSystemDataGlobal<__WNSystemData>::mData.mTotalPhysicalMemory);
}

WN_INLINE WN_UINT64 WNSystem::TotalVirtualMemory() {
    #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
        __WNSystemDataInitialize();
    #endif

    return(__WNSystemDataGlobal<__WNSystemData>::mData.mTotalVirtualMemory);
}

WN_INLINE WN_UINT64 WNSystem::AvailablePhysicalMemory() {
    WN_UINT64 availablePhysical = 0;

    #ifdef _WN_WINDOWS
        MEMORYSTATUSEX memoryInfo;

        memoryInfo.dwLength = sizeof(MEMORYSTATUSEX);

        GlobalMemoryStatusEx(&memoryInfo);

        availablePhysical = memoryInfo.ullAvailPhys;
    #elif defined _WN_LINUX
        struct sysinfo memoryInfo;

        if (sysinfo(&memoryInfo) == 0) {
            availablePhysical = memoryInfo.freeram;
            availablePhysical *= memoryInfo.mem_unit;
        }
    #endif

    return(availablePhysical);
}

WN_INLINE WN_UINT64 WNSystem::AvailableVirtualMemory() {
    WN_UINT64 availableVirtual = 0;

    #ifdef _WN_WINDOWS
        MEMORYSTATUSEX memoryInfo;

        memoryInfo.dwLength = sizeof(MEMORYSTATUSEX);

        GlobalMemoryStatusEx(&memoryInfo);

        availableVirtual = memoryInfo.ullAvailPageFile;
    #elif defined _WN_LINUX
        struct sysinfo memoryInfo;

        if (sysinfo(&memoryInfo) == 0) {
            availableVirtual = memoryInfo.freeram;
            availableVirtual += memoryInfo.freeswap;
            availableVirtual *= memoryInfo.mem_unit;
        }
    #endif

    return(availableVirtual);
}

WN_INLINE WN_BOOL WNSystem::IsHyperThreaded() {
    #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
        __WNSystemDataInitialize();
    #endif

    return(__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorHyperThreaded);
}

WN_INLINE WN_BOOL WNSystem::IsLittleEndian() {
    #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
        __WNSystemDataInitialize();
    #endif

    return(__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorEndianness == __WN_PROCESSOR_ENDIAN_LITTLE);
}

WN_INLINE WN_BOOL WNSystem::IsBigEndian() {
    return(!IsLittleEndian());
}

WN_INLINE WN_BOOL WNSystem::HasFPU() {
    #ifdef __WN_FPU_AVAILABLE
        return(WN_TRUE);
    #else
        #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
            __WNSystemDataInitialize();
        #endif

        return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_FPU) != 0);
    #endif
}

WN_INLINE WN_BOOL WNSystem::HasCMPXCHG8() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
            __WNSystemDataInitialize();
        #endif

        return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_CMPXCHG8) != 0);
    #endif
}

WN_INLINE WN_BOOL WNSystem::HasCMPXCHG16() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
            __WNSystemDataInitialize();
        #endif

        return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_CMPXCHG16) != 0);
    #endif
}

WN_INLINE WN_BOOL WNSystem::HasMMX() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
            __WNSystemDataInitialize();
        #endif

        return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_MMX) != 0);
    #endif
}

WN_INLINE WN_BOOL WNSystem::HasEMMI() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
            __WNSystemDataInitialize();
        #endif

        return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_EMMI) != 0);
    #endif
}

WN_INLINE WN_BOOL WNSystem::Has3DNow() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
            __WNSystemDataInitialize();
        #endif

        return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_3DNOW) != 0);
    #endif
}

WN_INLINE WN_BOOL WNSystem::Has3DNowPlus() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
            __WNSystemDataInitialize();
        #endif

        return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_3DNOW_PLUS) != 0);
    #endif
}

WN_INLINE WN_BOOL WNSystem::HasSSE() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifdef __WN_SSE_AVAILABLE
            return(WN_TRUE);
        #else
            #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
                __WNSystemDataInitialize();
            #endif

            return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_SSE) != 0);
        #endif
    #endif
}

WN_INLINE WN_BOOL WNSystem::HasSSE2() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifdef __WN_SSE2_AVAILABLE
            return(WN_TRUE);
        #else
            #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
                __WNSystemDataInitialize();
            #endif

            return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_SSE2) != 0);
        #endif
    #endif
}

WN_INLINE WN_BOOL WNSystem::HasSSE3() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifdef _WN_SSE3_AVAILABLE
            return(WN_TRUE);
        #else
            #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
                __WNSystemDataInitialize();
            #endif

            return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_SSE3) != 0);
        #endif
    #endif
}

WN_INLINE WN_BOOL WNSystem::HasSSSE3() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
            __WNSystemDataInitialize();
        #endif

        return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_SSSE3) != 0);
    #endif
}

WN_INLINE WN_BOOL WNSystem::HasSSE41() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
            __WNSystemDataInitialize();
        #endif

        return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_SSE41) != 0);
    #endif
}

WN_INLINE WN_BOOL WNSystem::HasSSE42() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
            __WNSystemDataInitialize();
        #endif

        return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_SSE42) != 0);
    #endif
}

WN_INLINE WN_BOOL WNSystem::HasSSE4a() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
            __WNSystemDataInitialize();
        #endif

        return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_SSE4A) != 0);
    #endif
}

WN_INLINE WN_BOOL WNSystem::HasCVT16() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
            __WNSystemDataInitialize();
        #endif

        return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_CVT16) != 0);
    #endif
}

WN_INLINE WN_BOOL WNSystem::HasVEXPrefix() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
            __WNSystemDataInitialize();
        #endif

        return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_VEX_PREFIX) != 0);
    #endif
}

WN_INLINE WN_BOOL WNSystem::HasAES() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
            __WNSystemDataInitialize();
        #endif

        return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_AES) != 0);
    #endif
}

WN_INLINE WN_BOOL WNSystem::HasCLMUL() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
            __WNSystemDataInitialize();
        #endif

        return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_CLMUL) != 0);
    #endif
}

WN_INLINE WN_BOOL WNSystem::HasFMA3() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
            __WNSystemDataInitialize();
        #endif

        return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_FMA3) != 0);
    #endif
}

WN_INLINE WN_BOOL WNSystem::HasFMA4() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
            __WNSystemDataInitialize();
        #endif

        return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_FMA4) != 0);
    #endif
}

WN_INLINE WN_BOOL WNSystem::HasXOP() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
            __WNSystemDataInitialize();
        #endif

        return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_XOP) != 0);
    #endif
}

WN_INLINE WN_BOOL WNSystem::HasAVX() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
            __WNSystemDataInitialize();
        #endif

        return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_AVX) != 0);
    #endif
}

WN_INLINE WN_BOOL WNSystem::HasAVX2() {
    #ifndef _WN_X86
        return(WN_FALSE);
    #else
        #ifndef WN_CORE_CONFIG_SYSTEM_EXPLICIT_INITILIZATION
            __WNSystemDataInitialize();
        #endif

        return((__WNSystemDataGlobal<__WNSystemData>::mData.mProcessorFeatures & __WN_PROCESSOR_FEATURE_X86_AVX2) != 0);
    #endif
}

#endif // __WN_CORE_SYSTEM_INL__